
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <ctype.h>

#include "soc.h"
#include "fatfs.h"






#define SDIF_TRANS_INT_DIS			0x100
#define SDIF_TRANS_ERR_CHK_EN		0x080			
#define SDIF_TRANS_MULTI			0x020
#define SDIF_TRANS_READ				0x010
#define SDIF_TRANS_BLK_CNT_EN		0x002
#define SDIF_TRANS_DMA_EN			0x001

#define SDIF_CMD_DATA               0x020
#define SDIF_CMD_IDX_CHECK          0x010
#define SDIF_CMD_CRC_CHECK          0x008

#define SDIF_CMD_RESP_136          	0x001
#define SDIF_CMD_RESP_48          	0x002
#define SDIF_CMD_RESP_48_BUSY      	0x003

#define SDIF_INT_XFER_COMPLETE		0x001
#define SDIF_INT_BUF_RD_READY 		0x020
#define SDIF_INT_ERROR 				0x8000






/*
 CMD9 : SEND_CSD
CMD10 : SEND_CID
ac, R2 : (CID, CSD register)

如果 iret == 0,  那么 pary (4 个 32bits) 返回 128 bits 寄存器.
否则 iret 表示错误码.
*/
int sdhc_ac_r2( cvi_sdhc_regs_t * psdhc, uint16_t rca, uint32_t * pary )
{
	uint32_t temp;
	 
	/* clear err int */
	psdhc->ErrorIntStatus = 0xFFFF;
	psdhc->NormalIntStatus = 0xFFFF;

	/* prepare */
	psdhc->BlkSize = 0;
	psdhc->Argument = (uint32_t)rca << 16;
	psdhc->TransMode = 0x10;
	psdhc->Command = (9ul << 8) | SDIF_CMD_CRC_CHECK | SDIF_CMD_RESP_136;

	/* wait respond */
	while ( true )  {

		temp = psdhc->NormalIntStatus;

		if ( temp & SDIF_INT_ERROR )  {
			temp = psdhc->ErrorIntStatus;
			return (int)temp;
		}

		if ( temp & SDIF_INT_XFER_COMPLETE )  {
			psdhc->NormalIntStatus = SDIF_INT_XFER_COMPLETE;
			break;
		}
	}

	// todo : check response.
	pary[0] = psdhc->Response[0];
	pary[1] = psdhc->Response[1];
	pary[2] = psdhc->Response[2];
	pary[3] = psdhc->Response[3];
	return 0;
}


/*
CMD3 : SEND_RELATIVE_ADDR, 
bc, R6 :
[31:16] : New published RCA
 [15:0] : card status bits: 23,22,19,12:0
*/
int sdhc_bc_r6( cvi_sdhc_regs_t * psdhc, uint32_t * prsp )
{
	uint32_t temp;
	
	/**/
	psdhc->ErrorIntStatus = 0xFFFF;
	psdhc->NormalIntStatus = 0xFFFF;

	/**/
	psdhc->BlkSize = 0;
	psdhc->Argument = 0;
	psdhc->TransMode = 0x10;
	psdhc->Command = (3ul << 8) | SDIF_CMD_IDX_CHECK | SDIF_CMD_CRC_CHECK | SDIF_CMD_RESP_48;

	/* wait .. */
	while ( true )  {

		temp = psdhc->NormalIntStatus;

		if ( temp & SDIF_INT_ERROR )  {
			temp = psdhc->ErrorIntStatus;
			return (int)temp;
		}

		if ( temp & SDIF_INT_XFER_COMPLETE )  {
			psdhc->NormalIntStatus = SDIF_INT_XFER_COMPLETE;
			break;
		}

	}

	// todo : check response.
	*prsp = psdhc->Response[0];
	return 0;
}



/*
CMD7 : ac, R1b :
如果 iret = 0, 表示成功, 那么输出值 pcds 是个 32 bits 的 card status.
否则 iret 表示错误码, 比如 cmd7 命令 rca = 0 的时候会等待超时..
*/
int sdhc_cmd7( cvi_sdhc_regs_t * psdhc, uint16_t rca, uint32_t * pcds )
{
	uint32_t temp;

	/* clear err int */
	psdhc->ErrorIntStatus = 0xFFFF;
	psdhc->NormalIntStatus = 0xFFFF;

	/* prepare */
	psdhc->BlkSize = 0;
	psdhc->Argument = (uint32_t)rca << 16;
	psdhc->TransMode = 0x10;
	psdhc->Command = (7ul << 8) | SDIF_CMD_IDX_CHECK | SDIF_CMD_CRC_CHECK | SDIF_CMD_RESP_48_BUSY;

	/* wait respond */
	while ( true )  {

		temp = psdhc->NormalIntStatus;

		if ( temp & SDIF_INT_ERROR )  {
			temp = psdhc->ErrorIntStatus;
			return (int)temp;
		}

		if ( temp & SDIF_INT_XFER_COMPLETE )  {
			psdhc->NormalIntStatus = SDIF_INT_XFER_COMPLETE;
			break;
		}
	}

	/**/
	*pcds = psdhc->Response[0];
	return 0;
}





int sdcard_init( uintptr_t shbase, sdctx_t * pctx )
{
	int iret;
	uint32_t tary[4];
	uint8_t tver;
	uint16_t rca;

	/**/
	pctx->psdhc = (cvi_sdhc_regs_t *)shbase;

	/* deselect card, idle state */
	iret = sdhc_cmd7( pctx->psdhc, 0, tary );
	printf( "cmd7, 0, ret = %d\n", iret );

	/**/
	iret = sdhc_bc_r6( pctx->psdhc, tary );
	if ( iret != 0 )  {
		printf( "new rca fail, %d\n", iret );
		return 234;
	}

	/**/
	rca = (uint16_t)( tary[0] >> 16 );
	iret = sdhc_ac_r2( pctx->psdhc, rca, tary );
	if ( iret != 0 )  {
		return 345;
	}

	/*
	parse : speed and capacity	
	sdhc 自动丢弃了末尾 8 bits, 所以整体向右移位 8 bits. 
	tary[3], 24bits : CSD [127:104]
	tary[2], 32bits : CSD [103: 72]
	tary[1], 32bits : CSD [ 71: 40]
	tary[0], 32bits : CSD [ 39:  8]
	*/

	tver = (tary[3] >> 22) & 0x3;
	
	if ( tver == 0 )  {

		uint32_t rblen;
		uint32_t csize;
		uint32_t cmult;
		uint64_t temp;

		// uint32_t speed;
		// speed = tary[2] >> 24;			/* [103:96] */
		
		rblen = (tary[2] >> 8) & 0xF;	/* [83:80] */
		rblen = 0x1 << rblen;

		csize = tary[2] & 0x3;			/* [ 73:62] */
		csize = (csize << 10) | ( (tary[1] >> 22) & 0x3FF );
		
		cmult = (tary[1] >> 7) & 0x7;	/* [ 49:47] */
		cmult = 0x1 << (cmult + 2);

		temp = csize + 1;
		temp = temp * cmult;
		temp = temp * rblen;

		//printf( "rblen = %u\n", rblen );
		//printf( "csize = %u\n", csize );
		//printf( "cmult = %u\n", cmult );
		pctx->capacity = temp / 512;

	} else if ( tver == 1 )  {

		uint32_t csize;

		csize = (tary[1] >> 8) & 0x3FFFFF;		/* [69:48] */
		csize = csize + 1;
		
		// printf( "struct version: 2.0\n" );
		// printf( "%lu\n", csize );
		pctx->capacity = (uint64_t)csize * 1024;

	} else if ( tver == 2 )  {
		return 222;
	} else {
		return 333;
	}


	/* 4*1024*1024 数量的 sector 是 2G Bytes */
	if ( pctx->capacity <= 4194304 )  {
		pctx->scflag = 1;
	} else {
		pctx->scflag = 0;
	}


	/* 重新执行 cmd7, 重新进入 trans state. */
	iret = sdhc_cmd7( pctx->psdhc, rca, tary );
	if ( iret != 0 )  {
		return 501;
	}

	return 0;
}


/*
CMD17, read single block 
*/
int sdcard_read_single( sdctx_t * pctx, uint32_t sidx, void * pary )
{
	cvi_sdhc_regs_t * psdhc = pctx->psdhc;
	uint32_t temp;
	uint32_t resp;
	uint32_t * pu32;

	/* clear err int */
	psdhc->ErrorIntStatus = 0xFFFF;
	psdhc->NormalIntStatus = 0xFFFF;

	/**/
	if ( pctx->scflag != 0 )  {
		psdhc->Argument = sidx * 512;
	} else {
		psdhc->Argument = sidx;
	}
	
	psdhc->BlkSize = 0x200;
	psdhc->TransMode = SDIF_TRANS_ERR_CHK_EN | SDIF_TRANS_READ;
	psdhc->Command = (17 << 8) | SDIF_CMD_DATA | SDIF_CMD_IDX_CHECK | SDIF_CMD_CRC_CHECK | SDIF_CMD_RESP_48;
	
	/* wait .. */
	while ( true )  {

		temp = psdhc->NormalIntStatus;
		if ( temp & SDIF_INT_XFER_COMPLETE )  {
			psdhc->NormalIntStatus = SDIF_INT_XFER_COMPLETE;
			break;
		}

		if ( temp & SDIF_INT_ERROR )  {
			printf( "error1 : %08lx\n", temp );
			return 22;
		}
	
		temp = psdhc->ErrorIntStatus;
		if ( temp != 0 )  {
			printf( "error2 : %08lx\n", temp );
			return 33;
		}
	}

	// todo : check response.
	resp = psdhc->Response[0];
	//printf( "resp : %08x\n", resp );

	/* wait .. */
	while ( true )  {
		temp = psdhc->NormalIntStatus;
		if ( temp & SDIF_INT_BUF_RD_READY )  {
			break;
		}
	}

	/**/
	pu32 = (uint32_t *)pary;
	for ( int i=0; i<128; i++ )  {
		pu32[i] = psdhc->BuffPort;
	}

	/**/
	psdhc->NormalIntStatus = SDIF_INT_BUF_RD_READY;
	return 0;
}
















#define __PACKED    __attribute__((packed, aligned(1)))

typedef struct _tag_part_entry  {
	uint8_t btflag;				/* boot Indicator */
	uint8_t sttchs[3];			/* start : cylinder, head, sector */
	uint8_t fstype;				/* Partition Type,  0x0B, 0x0C */
	uint8_t endchs[3];			/* End : cylinder, head, sector */
	uint32_t sttlba;			/* 开始位置, sector 单位. */
	uint32_t totals;			/* 分区容量, sector 单位. */
} part_entry_t;



/*
1. Reserved Region;  BPB 在第一个 sector.
2. FAT Region;  可以有多个 FAT 作为备份, 内容应该相同.
3. Root Directory Region; 根目录表.
4. Data Region;  文件或子目录内容区域.
*/


typedef struct __PACKED _tag_bpb_block  {
	uint8_t jmp_boot[3];  			/* 0xEB, 0x3C, 0x90 */
	uint8_t oem_name[8];			/* mkfs tools name */
	uint16_t bytes_per_sector;		/* Count of bytes per sector. */
	uint8_t sectors_per_clus;		/* Number of sectors per allocation unit. */
	uint16_t rsvd_sector_cnt;		/* Number of reserved sectors. */

	uint8_t num_fats;				/* count of file allocation tables (FATs)  */
	uint16_t root_entry_cnt;		/* root directory, entry count */
	uint16_t total_sec16;			/* zero, --> total_sec32 */
	uint8_t media_type;				/**/
	uint16_t fat_siz16;				/* fat sectors count */
	uint16_t sectors_per_track;		/* Sectors per track  for interrupt 0x13. */
	uint16_t num_of_head;			/* Number of heads for interrupt 0x13. */
	uint32_t hidden_sectors;

	uint32_t total_sec32;			/**/
	uint8_t  drive_number;			/* drive number for interrupt 0x13. */
	uint8_t  rsvd1;
	uint8_t  boot_signature;
	uint32_t vol_serialno;			/**/
	uint8_t  vol_label[11];
	uint8_t  filesys_type[8];		/**/

} bpb_block_t;



typedef struct _tag_dirent {

	uint8_t  file_name[11];		/* 8+3, */
	uint8_t  file_attr;			/* 0x10=DIRECTORY, 0x0F=LONG_NAME, 0x20=ARCHIVE */
	uint8_t  rsvd1;
	uint8_t  crt_timeten;		/* Count of tenths of a second.  */
	uint16_t  crt_time;			/* Granularity is 2 seconds */
	
	uint16_t  crt_date;			/* Creation date. */
	uint16_t  acc_date;			/* Last access date. */
	uint16_t  clus_high;		/* High word of first cluster number */
	uint16_t  wrt_time;			/* Last modification (write) time. */
	uint16_t  wrt_date;			/* Last modification (write) date. */
	uint16_t  clus_low;			/* Low word of first cluster number  */
	uint32_t  file_size;		/* size in bytes of file/directory described by this entry. */

} dirent_t;

/*
确定 分区表, 找到第一个 FAT 分区.
确定 header 格式, 目前认识 FAT16, 识别参数信息.
确定 FAT, DIR table 位置,  读入到缓冲区.

1. Master Boot Record (MBR), standard BIOS partition table.
*/
int fatfs_init( sdctx_t * psdc, fatfs_t * pfatfs )
{
	int iret;
	part_entry_t  partent;
	bpb_block_t * pbpb;
	uint8_t tary[512];

	/**/
	pfatfs->psdc = psdc;

	// check partion table.
	iret = sdcard_read_single( pfatfs->psdc, 0, tary );
	if ( iret != 0 )  {
		return 1;
	}

	// check tail signature.
	if ( (tary[510] != 0x55) || (tary[511] != 0xAA) ) {
		return 2;
	}

	//
	memcpy( (void *)&partent, &tary[446], sizeof(part_entry_t) );
	if ( partent.fstype != 0x0C ) {
		return 3;
	}

	// bpb : BIOS Parameter Block, 
	pfatfs->sttlba = partent.sttlba;
	pfatfs->totals = partent.totals;
	iret = sdcard_read_single( pfatfs->psdc, pfatfs->sttlba, tary );
	if ( iret != 0 )  {
		return 4;
	}

	// check tail signature.
	if ( (tary[510] != 0x55) || (tary[511] != 0xAA) ) {
		return 7;
	}

	// check, fs type.
	pbpb = (bpb_block_t *)tary;
	printf( "align? = %x\n", pbpb->bytes_per_sector );
	if ( pbpb->bytes_per_sector != 512 )  {
		return 51;
	}
	
	if ( 0 != memcmp( pbpb->filesys_type, "FAT16\x20\x20\x20", 8  ) )  {
		return 52;
	}

	if ( pfatfs->totals < pbpb->total_sec32 )  {
		return 53;
	}

	// root directory size;  (cnt * 32) / 512 = cnt / 16;
	// todo : check, 数据范围, 能否被 FAT 表管理.
	pfatfs->clusiz = pbpb->sectors_per_clus;
	pfatfs->fatofs = pbpb->rsvd_sector_cnt;
	pfatfs->fatsiz = pbpb->fat_siz16;
	pfatfs->rdrofs = pfatfs->fatofs + ( pfatfs->fatsiz * (uint32_t)pbpb->num_fats );
	pfatfs->rdrsiz = pbpb->root_entry_cnt / 16;
	pfatfs->datofs = pfatfs->rdrofs + pfatfs->rdrsiz;
	return 0;
}

/*
FAT 文件系统,  目录结构中文件名使用 8+3 格式, Short File Names. 
name 部分不足 8 个字符, 使用 0x20 补齐.
ext 部分不足 3 个字符, 也是用 0x20 补齐.
隐含分隔符 '.' = 0x2E.
并且 name, ext 其中的英文字母部分全部是大写格式.
*/
static int fname_match( uint8_t * pfn83, const char * fname )
{
	int idx;
	int ilen;
	char * ptr;
	uint8_t tname[11];

	ilen = strlen( fname );
	if ( (ilen <= 0) || (ilen > 12) )  {
		return 11;
	}

	// fill by 0x20
	memset( tname, 0x20, 11 );

	/* 确实是否有分隔符. */
	ptr = strchr( fname, 0x2E );
	if ( ptr == NULL )  {

		if ( ilen > 8 )  {
			return 22;
		}

		// only name part, convert to Upper Case.
		for ( int i=0; i<ilen; i++ )  {
			tname[i] = toupper( (uint8_t)fname[i] );
		}

	} else {

		idx = ptr - fname;
		if ( idx > 8 )  {
			return 23;
		}

		// check ext too long ??
		if ( (ilen - idx ) > 4  )  {
			return 24;
		}

		// 
		for ( int i=0; i<idx; i++ )  {
			tname[i] = toupper( (uint8_t)fname[i] );
		}

		for ( int i=0; i< (ilen - idx); i++ )  {
			tname[8+i] = toupper( (uint8_t)fname[idx + 1 + i] );;
		}
		
	}

	return memcmp( pfn83, tname, 11 );
}


static int fatfs_find_dirent( fatfs_t * pfatfs, const char * fname, dirent_t * pent )
{
	int iret;
	dirent_t * pdent;
	uint32_t rdrpos;
	uint8_t tary[512];

	// read RootDirectory sector-0.
	rdrpos = pfatfs->sttlba + pfatfs->rdrofs;
	iret = sdcard_read_single( pfatfs->psdc, rdrpos, tary );
	if ( iret != 0 )  {
		return 1;
	}

	// todo : search Root Directory
	pdent = (dirent_t *)tary;
	for ( int i=0; i<16; i++ )  {

		if ( pdent[i].file_attr != 0x20 )  {
			continue;
		}

		if ( 0 != fname_match( pdent[i].file_name, fname ) )  {
			continue;
		}

		// dir entry copy
		memcpy( pent, &pdent[i], sizeof(dirent_t) );
		return 0;
	}

	return 33;
}


static int fatfs_full_chain( fatfs_t * pfatfs, uint16_t clush, uint16_t * pary ) 
{
	int iret;
	int idx;
	uint32_t temp;
	uint32_t fatpos;
	uint16_t clusmax;
	uint16_t fatbl[256];


	// xxxx
	clusmax = (uint16_t)( (pfatfs->totals - pfatfs->rdrofs) / pfatfs->clusiz );
	fatpos = 0;
	pary[0] = clush;
	
	printf( "first chain : %x\n", clush );
	printf( "fatsiz = %lu\n", pfatfs->fatsiz );
	printf( "clusmax = %u\n", clusmax );

	// 
	for ( idx=1; ; idx+=1 )  {

		// 
		temp = pfatfs->sttlba + pfatfs->fatofs + (clush / 256);

		if ( fatpos != temp )  {
			
			iret = sdcard_read_single( pfatfs->psdc, temp, fatbl );
			if ( iret != 0 )  {
				return -200;
			}

			fatpos = temp;
			printf( "fat pos : %lx\n", temp );
		}

		// get next.
		temp = clush % 256;
		clush = fatbl[temp];
		// printf( "nxt chain : %x\n", clush );

		// check end..
		if ( clush >= 0xFFF8 )  {
			break;
		}

		// 非法值.
		if ( (clush >= clusmax) || (clush < 2) )  {
			return -201;
		}

		// list 超长 ??
		if ( idx >= 500 )  {
			return -202;
		}

		// 正常, next chain.
		pary[idx] = clush;
	}

	/**/
	return idx;
}


/*
仅仅支持根目录, 不搜索下一级目录.
确定文件是否存在, 返回文件长度??...
*/
int fatfs_fstat( fatfs_t * pfatfs, const char * fname, uint32_t * psiz )
{
	int iret;
	dirent_t dent;

	iret = fatfs_find_dirent( pfatfs, fname, &dent );
	if ( iret != 0 )  {
		// not found..
		return 123;
	}

	//
	*psiz = dent.file_size;
	return 0;
}


/*
读文件的整个内容, 到指定内存位置.
返回文件长度??
*/
int fatfs_fread( fatfs_t * pfatfs, const char * fname, uint8_t * ptr )
{
	int iret;
	int iclus;
	uint32_t tsiz;
	dirent_t dent;
	uint32_t secpos;
	uint16_t chain[512];

	iret = fatfs_find_dirent( pfatfs, fname, &dent );
	if ( iret != 0 )  {
		// not found..
		return -123;
	}

	if ( dent.clus_high != 0 )  {
		return -124;
	}

	// 
	iclus = fatfs_full_chain( pfatfs, dent.clus_low, chain );
	if ( iclus <= 0 )  {
		printf( "full chain : %d\n", iclus );
		return -125;
	}

	tsiz = 0;
	for ( int i=0; i<iclus; i++ )  {

		// printf( "chain[%d] : %u\n", i, chain[i] );

		secpos = (uint32_t)(chain[i] - 2) * pfatfs->clusiz;
		secpos = pfatfs->sttlba + pfatfs->datofs + secpos;
		// printf( "secpos : %lu\n", secpos );

		// 每个 cluster 是由若干 sector 组成.
		for ( int j=0; j< (int)pfatfs->clusiz; j++ )  {
			
			iret = sdcard_read_single( pfatfs->psdc, (secpos + j), ptr );
			if ( iret != 0 ) {
				return -200;
			}

			ptr += 512;
		}
		
		// total size...
		tsiz += pfatfs->clusiz * 512;
	}

	return tsiz;
}





